package xyz.erupt.flow.core.engine.delegate;

import org.camunda.bpm.engine.RepositoryService;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.TaskService;
import org.camunda.bpm.engine.delegate.*;
import org.camunda.bpm.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import xyz.erupt.core.exception.EruptApiErrorTip;
import xyz.erupt.flow.bean.entity.OaWorkFlowDefinition;
import xyz.erupt.flow.bean.entity.node.OaProcessNode;
import xyz.erupt.flow.bean.entity.node.OaProcessNodeCondition;
import xyz.erupt.flow.bean.entity.node.OaProcessNodeGroup;
import xyz.erupt.flow.bean.entity.node.OaProcessNodeProps;
import xyz.erupt.flow.repository.EruptUserRepository;
import xyz.erupt.flow.service.WorkFlowDefinitionService;
import xyz.erupt.upms.model.EruptOrg;
import xyz.erupt.upms.model.EruptRole;
import xyz.erupt.upms.model.EruptUser;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * @author PP
 * @version 1.0
 * @project flow
 * @description
 * @date 2024/10/30 09:57:37
 */
@Service("InclusiveGatewayDelegate")
public class InclusiveGatewayDelegate implements JavaDelegate {

    @Autowired
    private EruptUserRepository eruptUserRepository;
    @Autowired
    private WorkFlowDefinitionService definitionService;
    @Autowired
    private RepositoryService repositoryService;


    /**
     * 判断是否（判断发起人是否为指定的人）
     * @return
     */
    public boolean checkStatus(OaProcessNodeProps oaProcessNodeProps,DelegateExecution delegateExecution){
        //进行发起人条件判断
        if (oaProcessNodeProps.isDefault()){//默认条件
            return true;
        }
        List<Boolean> booleans = checkGroup(oaProcessNodeProps,delegateExecution);//判断
        if ("AND".equals(oaProcessNodeProps.getGroupsType()))//组关系（且）
        {
            return booleans.contains(false)?false:true;
        }else{//或
            return booleans.contains(true)?true:false;
        }
    }

    /**
     * 返回条件列表
     * @param oaProcessNodeProps
     * @return
     */
    private List<Boolean> checkGroup(OaProcessNodeProps oaProcessNodeProps,DelegateExecution delegateExecution) {
        ArrayList<Boolean> booleansPa = new ArrayList<Boolean>();//储存条件结果
        if(oaProcessNodeProps.getGroups()!=null && !oaProcessNodeProps.getGroups().isEmpty()){
            //进行条件判断
            for (OaProcessNodeGroup group : oaProcessNodeProps.getGroups()) {
                ArrayList<Boolean> booleans = new ArrayList<Boolean>();//储存条件结果
                if(group.getConditions()!=null && !group.getConditions().isEmpty()){
                    for (OaProcessNodeCondition condition : group.getConditions()) {
                        booleans.add(checkChildren(condition,delegateExecution));
                    }
                }else {//无条件添加通过
                    booleans.add(true);
                }
                if ("AND".equals(group.getGroupType())){
                    boolean hasFalse = booleans.contains(false);
                    booleansPa.add(hasFalse?false:true);
                }else {
                    boolean hasTrue = booleans.contains(true);
                    booleansPa.add(hasTrue);
                }
            }
        }else {//无任何条件则直接通过
            booleansPa.add(true);

        }
        return booleansPa;
    }

    /**
     * 判断用户条件
     * @param condition
     * @return
     */
    private Boolean checkChildren(OaProcessNodeCondition condition,DelegateExecution delegateExecution) {
        //查找发起人
        TaskService taskService = delegateExecution.getProcessEngineServices().getTaskService();
        List<Task> list = taskService.createTaskQuery()
                .processInstanceId(delegateExecution.getProcessInstanceId())
                .list();
        Object[] array = list.stream().filter(s -> "发起人".equals(s.getName())).toArray();
        Task task = array.length > 0? (Task) array[0]:null;

        if (task==null){
            throw new EruptApiErrorTip("该流程错误！");
        }
        //查询角色id
        EruptUser eruptUser = eruptUserRepository.findById(Long.getLong(task.getAssignee())).orElseThrow(() -> new EruptApiErrorTip("不存在该人员"));
        if ("User".equals(condition.getValueType())){//为用户之一
            if (condition.getValue()!=null){
                return Arrays.asList(condition.getValue()).contains(task.getAssignee());
            }else {
                return true;//无任何备选人，则直接通过
            }
        }else if("Role".equals(condition.getValueType())){//为角色之一

            if (condition.getValue()!=null){
                Set<EruptRole> roles = eruptUser.getRoles();
                if (roles!=null && !roles.isEmpty()){
                    return roles.stream().anyMatch(Arrays.asList(condition.getValue())::contains);
                }else {
                    return false;//无角色返回false
                }
            }else {//无限定角色直接通过
                return true;
            }

        }else {//为部门或下属部门之一
            return checkOrg(eruptUser.getEruptOrg(),condition.getValue());
        }
    }

    private Boolean checkOrg(EruptOrg eruptOrg, String[] value) {
        if (value==null || value.length==0){
            return true;//无限定部门
        }else {
            if(eruptOrg!=null){
                //有组织
                boolean check = Arrays.asList(value).contains(eruptOrg.getId());//本级
                if (!check)//本级没有
                {
                    return checkOrg(eruptOrg.getParentOrg(),value);//上级
                }else {
                    return true;
                }
            }else {
                return false;//无组织，返回不通过
            }
        }
    }


    @Override
    public void execute(DelegateExecution execution) throws Exception {
        //设置默认变量status(true or false)
        String key = repositoryService.getProcessDefinition(execution.getProcessDefinitionId()).getKey();
        Long processDefinitionId = Long.parseLong(key.replace("_", ""));//获取当前流程定义id（与workflowId同一个，格式："_Id"）
        String currentActivityId = execution.getCurrentActivityId();//获取连线id
        OaWorkFlowDefinition workFlowDefinition = definitionService.getById(processDefinitionId);
        OaProcessNode currentNode = workFlowDefinition.getProcessNode().findNodeById(currentActivityId);
        if (currentNode!=null){
            OaProcessNodeProps props = currentNode.getProps();
            boolean status = checkStatus(props,execution);//判断条件
            execution.setVariable("status", status);//设置流程变量,条件中使用${status}解析使用
        }else {//无条件继续执行
            execution.setVariable("status", true);//设置流程变量,条件中使用${status}解析使用
        }
    }
}
